package org.wak.scene
{
	
	import flash.display.DisplayObject;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.system.System;
	import flash.text.TextField;
	
	import gs.TweenLite;
	
	import org.wak.baseoneonline.flash.astar.AStar;
	import org.wak.baseoneonline.flash.astar.AStarNode;
	import org.wak.baseoneonline.flash.geom.IntPoint;
	import org.wak.baseoneonline.flash.map.MapView;
	import org.wak.baseoneonline.flash.map.TileMap;
	import org.wak.character.AdventureCharacter;
	import org.wak.character.display.AbstractCharacterDisplay;
	import org.wak.main.AdventureMaster;
	import org.wak.scene.events.SceneEvent;
	

	/**
	* Scene is used to manage the logic of a scene inside the game
	*  
	*/
	public class Scene extends Sprite
	{
		// background of the scene
		private var mBackground:DisplayObject = null;
		
		// the player's character.
		private var mPlayerCharacter:AdventureCharacter = null ;
		
		// player display of the player's avatar on the scene.
		private var mPlayerDisplay:AbstractCharacterDisplay = null;
		
		// blocked for walk IntPoint s array.
		private var mBlockedForWalkTiles:Array = null;
		
		// non asociative array of interactables on the scene . 
		private var mInteractablesArray:Array = null ;
		
		// non asociative array of depth managed object in the Scene. 
		private var mDepthManagedObjectsArray:Array = null ;
		
		// an asoc array of SceneItems  item id -> LItem 
		private var mItemsAsocArray:Array = null ; 
		
		// array of GateSpots on the scene.
		private var mGateSpotsArray:Array = null ;
		
		// array of ActionSpots on the scene.
		private var mActionSpotsArray:Array = null ;
		
		// player starting tile
		private var mPlayerStartTile:IntPoint = null;
		
		// player walk speed
		private var mPlayerWalkSpeed:Number = 10; 
		
		// Size in pixels per tile
		private var mTileSize:Number = 16;
		
		// Tile map used for pathfinding.
		private var mTileMap:TileMap ;
		
		// Scene debug mode enabled
		private var mDebugEnabled:Boolean = false;
		
		// textfield to show the tile and mouse coords.
		private var mDebugTextField:TextField = null ;
		private var mDebugTextFieldShift:Number = 0 ;
		
		// array to collect tiles to send them to the clipboard.
		// until we have an scene editor.
		private var mDebugTilesCollected:Array = null ;
		
		// Scale decremente rate, for each pixel away from the camera the scale will be decreased
		// 1X this value. 
		private var mScaleRate:Number ;
		
		// This value is added to the scaling formula to give a greater max value of scaling.
		private var mScaleBase:Number ;
		
		// walk behind arrays.
		private var mWalkBehindAreasArray:Array ;
		
		// this is only for debugging purposes.
		private var mMapView:MapView ;
		
		// Where the path will be drawn, just for debugging purposes.
		private var mOverlay:Sprite;
		
		// Show where we click, for debug.
		private var mStartCursor:Shape;
		private var mGoalCursor:Shape;
		
		// Our start and goal positions
		private var mStart:IntPoint;
		private var mGoal:IntPoint;
		
		// Index of the walking point array mHeroWalkPoints.
		private var mWalkIndex:int = 0;
		
		// List of walking point created by the pathfinding after the click.
		private var mHeroWalkPoints:Array ;
		
		// game stage width, set when the scene is added to the stage.
		protected static var mStageWidth:Number = 0;
		// game stage height, set when the scene is added to the stage.
		protected static var mStageHeight:Number = 0;
		
		// local var of the resource manager.
		protected var mResMngr:Class = null;
		
		// WalkBehindArea currently active.
		protected var mActiveWalkBehind:WalkBehindArea = null;
		
		// Ambiental sound of the scene.
		protected var mAmbientSound:Sound = null ;
		
		// Ambiental sound channel of the scene.
		protected var mAmbientSoundChan:SoundChannel = null ;
		
		// the scene id .
		protected var mSceneId:String = "";
		
		// The selected scene interactable object.
		protected var mCurrentSelectedInteractable:ISceneInteractable = null ;
		
		// Array of other characters on the scene.
		protected var mCharactersOnScene:Array = null;
	
		// callback function for when the player finished to walk.
		protected var mOnPlayerWalkCompleteFunc:Function = null ;
		
		protected var mDrawLayer:Sprite = null ;
		protected var mBookModeEnabled:Boolean = false ;
		
		/**
		 * Constructor  
		 * 
		 * @param SceneDefinition wich defines the main Scene's data.
		 */
		public function Scene(pDef:ISceneDefinition)
		{
			super();
			if( pDef == null )
				throw new Error("null SceneDefinition");
				
				
			mResMngr = AdventureMaster.Resources ;
			
			mBackground = new mResMngr[pDef.backgroundId] ;
			
			mPlayerCharacter = AdventureMaster.CharactersMngr.GetCharacter( pDef.playerCharacter );
			mPlayerDisplay = mPlayerCharacter.characterDisplay ;
			
			
			
			mBlockedForWalkTiles = pDef.blockedForWalkArray ;
			mTileSize = pDef.tileSize ;
			mPlayerStartTile = pDef.playerStartingPoint ;
			mPlayerWalkSpeed = pDef.playerWalkSpeed ;
			mPlayerDisplay.moveSpeed = mPlayerWalkSpeed ;
			mDebugEnabled = pDef.debugEnabled ;
			mScaleRate = pDef.scaleRate ;
			mScaleBase = pDef.scaleBase ;
			
			mItemsAsocArray = pDef.sceneItems ;
			
			
			// Load the WalkBehindAreas.
			mWalkBehindAreasArray = new Array();
			mDepthManagedObjectsArray = new Array();
			var fNewWBehindArea:WalkBehindArea = null ;
			for each( var wbdef:IWalkbehindDefinition in pDef.walkBehinds)
			{
				fNewWBehindArea = new WalkBehindArea( wbdef );
				mWalkBehindAreasArray.push( fNewWBehindArea );
			}
			if(mWalkBehindAreasArray.length > 1 )
				mWalkBehindAreasArray.sort(SortPerBaseline );
				
			//trace( mWalkBehindAreasArray );
			
			this.addChild(mBackground);
			
			if(pDef.ambientalSoundId != "")
				mAmbientSound = new AdventureMaster.Resources[pDef.ambientalSoundId] as Sound ;
			
			mSceneId = pDef.sceneId ;
			
			mGateSpotsArray = pDef.gatespots ;
			
			mActionSpotsArray = pDef.actionspots ;
			
			// load the other characters on the scene.
			mCharactersOnScene = new Array();
			var fNewChar:AdventureCharacter = null ;
			for each( var charData:SceneCharData in pDef.charactersOnScene )
			{
				fNewChar = AdventureMaster.CharactersMngr.GetCharacter( charData.characterId );
				fNewChar.characterDisplay.x = charData.sceneX ;
				fNewChar.characterDisplay.y = charData.sceneY ;
				fNewChar.characterDisplay.SetAnimBehaviour( charData.behaviour );
				fNewChar.characterDisplay.SetInteractionPoint( charData.interactionPointX, charData.interactionPointY );
				fNewChar.characterDisplay.SetInteractionPointBehaviour( charData.interactionBehaviour );
				fNewChar.characterDisplay.sceneId = mSceneId ;
				mCharactersOnScene[fNewChar.characterId] = fNewChar;
			}
			mCharactersOnScene[mPlayerCharacter.characterId] = mPlayerCharacter ;
			
			Init();
		}
		
		/**
		 * If there are two scenes connected by a GateSpots. If the player came from scene A
		 * the player display will be placed near the GateSpot which jumps back to the scene A.
		 * If there is no corresponding GateSpot no change of coordinates will be made.
		 *    
		 * @param pFromSceneId Scene id where the player came from.
		 * 
		 */		
		public function SetPlayerDisplayFromScene( pFromSceneId:String ):void
		{
			//trace("SetPlayerDisplayFromScene");
			var myFilterFunction:Function = function(obj:GateSpot, index:int, array:Array):Boolean 
			{
	         	if(obj.destinationSceneId == pFromSceneId)
	         		return true ;
	         		
	         	return false ;
	        };

			var fResult:Array = mGateSpotsArray.filter(myFilterFunction);
			var fGSpot:GateSpot = null ;
			
			if( fResult.length > 0 )
			{
				fGSpot = fResult[0] as GateSpot ;
				mPlayerDisplay.ResetPosition( fGSpot.x + (fGSpot.height / 2), fGSpot.y + (fGSpot.width / 2) ) ;
				//trace("mPlayerDisplay:"+mPlayerDisplay.x + " - "+ mPlayerDisplay.y)
				
				// Update the player diplay current tile with the new position.
				mAuxUpdatedCurrentTile = screenToMap( mPlayerDisplay.x, mPlayerDisplay.y);
				mAuxUpdatedCurrentTile.x = Math.floor(mPlayerDisplay.x/mTileSize) ;
				mAuxUpdatedCurrentTile.y = Math.floor(mPlayerDisplay.y/mTileSize) ;
				mPlayerDisplay.ResetTilePosition( mAuxUpdatedCurrentTile.x, mAuxUpdatedCurrentTile.y );
				
				InitScroll();
				UpdatePerspective();
				
			}
			
		}
		
		
		// Auxiliary IntPoint to avoid reallocate a new instance in the update.
		private var mAuxUpdatedCurrentTile:IntPoint = new IntPoint();
		/**
		 * Main Loop Update method.
		*/
		public function Update(event:Event):void
		{
			
			// Update the current tile of the player renderer.
			var fUpdatedCurrentTile:IntPoint ;
			
			UpdateCharacters();
			UpdatePerspective();
			UpdateDepth();
			
			if( mPlayerDisplay.isMoving )
			{
				mAuxUpdatedCurrentTile = screenToMap( mPlayerDisplay.x, mPlayerDisplay.y);
				
				mAuxUpdatedCurrentTile.x = Math.floor(mPlayerDisplay.x/mTileSize) ;
				mAuxUpdatedCurrentTile.y = Math.floor(mPlayerDisplay.y/mTileSize) ;
				
				if( !(mPlayerDisplay.CurrentTileIsEqual(mAuxUpdatedCurrentTile) ))
				{
					mPlayerDisplay.SetCurrentTile( mAuxUpdatedCurrentTile.x, mAuxUpdatedCurrentTile.y );
				}
				
				
				UpdateScroll();
			}
			
		}
		
		private function UpdateCharacters():void
		{
			var fAuxUpdatedTile:IntPoint = null ;
			var charDisp:AbstractCharacterDisplay = null;
			for each( var char:AdventureCharacter in mCharactersOnScene)
			{
				charDisp = char.characterDisplay ;
				if(charDisp.isMoving)
				{
					fAuxUpdatedTile = screenToMap( charDisp.x, charDisp.y);
					fAuxUpdatedTile.x = Math.floor(charDisp.x/mTileSize) ;
					fAuxUpdatedTile.y = Math.floor(charDisp.y/mTileSize) ;
					
					if( !(charDisp.CurrentTileIsEqual(fAuxUpdatedTile) ))
					{
						charDisp.SetCurrentTile( fAuxUpdatedTile.x, fAuxUpdatedTile.y );
					}	
				}			
			}
		}
		
		/**
		 * Update the Depth position of the Depth managed objects. 
		 * 
		 */		
		protected function UpdateDepth():void
		{
			mDepthManagedObjectsArray.sort(SortPerBaseline);
			var fIndex:int = 1 ;
			for each( var inter:IManagedDepth in mDepthManagedObjectsArray)
			{
				if( fIndex <= this.numChildren -1)
					setChildIndex(inter.displayObject, fIndex);
				fIndex++ ;
			}
		}
		
		
		
		private function SortPerBaseline( pA:Object, pB:Object):int
		{
			if( pA.baseline > pB.baseline )
			{
				return 1 ;
			}
			else if( pA.baseline < pB.baseline )
			{
				return -1 ;
			}
			
			return 0 ;
		}
		
		
		/**
		 * Updates the scroll of the Scene accordly to the player's position.
		 */
		protected function UpdateScroll():void
		{
			//trace("UpdateScroll");
			// if the background dimensions aren't bigger than the stage 
			// this method ends here.
			if( (mBackground.width <= mStageWidth) && (mBackground.height <= mStageHeight) )
			{
				return ;
			}
			
			// For horizontal scroll
			///////////////////////////////////////////////////////
			var fGlobalPlayerX:Number = mPlayerDisplay.x ;
			
			if(this.x < 0)
			{
				fGlobalPlayerX = mPlayerDisplay.x + this.x ;
			}
			
			if( (fGlobalPlayerX > (mStageWidth/2)) && (mPlayerDisplay.isMoving) )
			{
				
				//trace("px:"+mPlayerDisplay.x + " sw/2:"+mStageWidth/2 + " global:"+fGlobalPlayerX);
				// is moving right.
				if( mPlayerDisplay.deltaX > 0 )
				{
					if( (this.x + mBackground.width) > mStageWidth)
						this.x -= Math.abs(mPlayerDisplay.deltaX) ;
				}
			}
			else if( (fGlobalPlayerX < (mStageWidth/2)) && (mPlayerDisplay.isMoving) )
			{
				//trace("px:"+mPlayerDisplay.x + " sw/2:"+mStageWidth/2 + " global:"+fGlobalPlayerX);
				// is moving left. 
				if( mPlayerDisplay.deltaX < 0 )
				{
					if(this.x < 0)
						this.x += Math.abs(mPlayerDisplay.deltaX) ;
				}
			}
			///////////////////////////////////////////////////////
			
			// for vertical scroll
			///////////////////////////////////////////////////////
			var fGlobalPlayerY:Number = mPlayerDisplay.y ;
			
			if(this.y < 0)
			{
				fGlobalPlayerY = mPlayerDisplay.y + this.y ;
			}
			
			if( (fGlobalPlayerY > (mStageHeight/2)) && (mPlayerDisplay.isMoving) )
			{
				
				//trace("py:"+mPlayerDisplay.y + " sw/2:"+mStageHeight/2 + " global:"+fGlobalPlayerY+ " this.y:"+this.y);
				// Is Moving down.
				if( mPlayerDisplay.deltaY > 0 )
				{
					if( (this.y + mBackground.height) > mStageHeight)
						this.y -= Math.abs(mPlayerDisplay.deltaY) ;
				}
			}
			else if( (fGlobalPlayerY < (mStageHeight/2)) && (mPlayerDisplay.isMoving) )
			{
				//trace("py:"+mPlayerDisplay.y + " sw/2:"+mStageHeight/2 + " global:"+fGlobalPlayerY+ " this.y:"+this.y);
				//is moving up 
				if( mPlayerDisplay.deltaY < 0 )
				{
					if(this.y < 0)
						this.y += Math.abs(mPlayerDisplay.deltaY) ;
				}
			}
			//trace("this.x:"+this.x);
			///////////////////////////////////////////////////////	
			if(mDebugEnabled)
			{
				mDebugTextField.x = this.x * (-1) + mDebugTextFieldShift;
				mDebugTextField.y = this.y * (-1) ;
			}
			
		}
		
		protected function InitScroll():void
		{
			//trace("InitScroll mPlayerDisplay:"+mPlayerDisplay.x + " - " +mPlayerDisplay.y);
			// if the background dimensions aren't bigger than the stage 
			// this method ends here.
			if( (mBackground.width <= mStageWidth) && (mBackground.height <= mStageHeight) )
			{
				return ;
			}
			
			if(mPlayerDisplay.y > mStageHeight)
			{
				this.y = (mStageHeight )  - (mPlayerDisplay.y )  - mStageHeight/4;
				//trace("Init scroll y:"+ this.y + "this.y + mBackground.height "+(this.y + mBackground.height) + "mStageHeight:"+mStageHeight );
				if( (this.y + mBackground.height) < mStageHeight)
					this.y -= (this.y + mBackground.height)-mStageHeight ;
			}
			
			if(mPlayerDisplay.x > mStageWidth)
			{
				this.x = (mStageWidth ) - (mPlayerDisplay.x) - mStageWidth/4;
				//trace(this.x);
				//trace("Init scroll x:"+ this.x + " this.x + mBackground.w "+(this.x + mBackground.width) + "mStageWidth:"+mStageWidth + " diff:"+ (mStageWidth - (this.x + mBackground.width)) );
				if( (this.x + mBackground.width) < mStageWidth)
					this.x += mStageWidth - (this.x + mBackground.width) ;
				//trace("Init scroll x:"+ this.x + " this.x + mBackground.w "+(this.x + mBackground.width) + "mStageWidth:"+mStageWidth + " diff:"+ (mStageWidth - (this.x + mBackground.width)) );
			}
			
		}
		
		/**
		 * Updates the PlayerRenderer Size accordly to its position in the Scene.
		 */
		protected function UpdatePerspective():void
		{
			
			
			//var fScaleRate:Number = 0.002;
			var fScaleRate:Number = mScaleRate ; 
			var fScaleBase:Number = mScaleBase ;
			
			var fDistanceFromCamera:Number = mBackground.height - mPlayerDisplay.y ;
			
			var fScaleValue:Number = (1 - (fDistanceFromCamera * fScaleRate)) + fScaleBase ;
			
			if(fScaleValue <= 0)
				fScaleValue = 0.1 ;
			
			if(mPlayerDisplay.perspectiveEnabled)
			{
				mPlayerDisplay.scaleX = fScaleValue ; 
				mPlayerDisplay.scaleY = fScaleValue ;
			}
			
			for each( var charDisp:AdventureCharacter in mCharactersOnScene)
			{
				fDistanceFromCamera = mBackground.height - charDisp.characterDisplay.y ;
				fScaleValue = (1 - (fDistanceFromCamera * fScaleRate)) + fScaleBase;
				
				if(charDisp.characterDisplay != mPlayerDisplay)
				{
					// if the char display is perspective enabled. Update.
					if(charDisp.characterDisplay.perspectiveEnabled)
					{
						charDisp.characterDisplay.scaleX = fScaleValue ;
						charDisp.characterDisplay.scaleY = fScaleValue ;
					}
				}
			}
			
			//trace(" fDistanceFromCamera:"+fDistanceFromCamera+ " scale:"+mPlayerDisplay.scaleX )
		}
		
		/**
		 * Initialize internal stuff to set the Scene to run.  
		 */
		protected function Init():void
		{
			//trace("Init");
			// use the image background to determine the size of the walkable tile map.
			var fTileMapW:int = Math.round( mBackground.width / mTileSize ) + 1;
			var fTileMapH:int = Math.round( mBackground.height / mTileSize ) + 1;
			
			// init the tile map
			mTileMap = new TileMap(fTileMapW,fTileMapH);
			SetWalkBlockedTiles(mTileMap, mBlockedForWalkTiles);
			
			// init the view for debugging.
			mMapView = new MapView(mTileMap, mTileSize);
			mMapView.alpha = 0.5 ;		
			if(mDebugEnabled)
			{	
				mDebugTilesCollected = new Array();
				this.addChild( mMapView );
				mMapView.draw() ;
				// init the overlay to see the path drawn, just for debugging.
				mOverlay = new Sprite();
				this.addChild( mOverlay );
			}
			
			
			if(mDebugEnabled)
			{
				// Create the cursors
				mStartCursor = createCursor(mTileSize-2, 0x00FF00);
				this.addChild( mStartCursor );
				mGoalCursor = createCursor(10, 0xFF00FF);
				this.addChild( mGoalCursor );
				mDebugTextField = new TextField();
				mDebugTextField.selectable = false ;
				mDebugTextField.width = 90 ;
				mDebugTextField.height = 40 ;
				mDebugTextField.border = true ;
				mDebugTextField.background = true ;
				mDebugTextField.backgroundColor = 0xFFFFFF ;
				this.addChild(mDebugTextField);
			}
			
			// Add Player Renderer.
			mPlayerDisplay.x = mapToScreenCoord( mPlayerStartTile.x );
			mPlayerDisplay.y = mapToScreenCoord( mPlayerStartTile.y );
			mPlayerDisplay.SetCurrentTile( mPlayerStartTile.x, mPlayerStartTile.y );
			
			
			mInteractablesArray = new Array();
			// Add items to the Scene
			for each( var item:SceneItem in mItemsAsocArray )
			{
				addInteractableItem(item) ;
				item.sceneId = mSceneId ;
			}
			// Add the player display to the interactables array.
			mInteractablesArray.push(mPlayerDisplay);
			// Add the player display to the depth managed objects array
			//mDepthManagedObjectsArray.push( mPlayerDisplay );
			
			// Add walkbehinds to the Scene.
			for each( var wba:WalkBehindArea in mWalkBehindAreasArray )
			{
				this.addChild(wba);
				// We push it to this array so the depth in the Scene is managed.
				mDepthManagedObjectsArray.push( wba );
			}
			
			// Add GateSpots to the Scene.
			for each( var gspot:GateSpot in mGateSpotsArray )
			{
				//trace("Gatespots added");
				this.addChild(gspot);
				mInteractablesArray.push(gspot);
			}
			
			//Add ActionSpots to the Scene
			for each( var aspot:ActionSpot in mActionSpotsArray )
			{
				//trace("Gatespots added");
				this.addChild(aspot);
				aspot.sceneId = mSceneId ;
				mInteractablesArray.push(aspot);
			}
			
			// Add characters to the scene.
			for each( var char:AdventureCharacter in mCharactersOnScene)
			{
				this.addInteractableItem(char.characterDisplay);
			}
			
			//trace( "mPlayerDisplay:" + mPlayerDisplay.x + " " + mPlayerDisplay.y);
			this.addEventListener(Event.ADDED_TO_STAGE, OnAddedToStage);
			
		}
		
		
		/**
		 * Removes an  ISceneInteractable from the scene
		 * @param pItem ISceneInteractable to remove.
		 * 
		 */		
		public function RemoveInteractable(pItem:ISceneInteractable):void
		{
			//trace("RemoveInteractable:"+pItem.interactableId);
			var fIndexInter:int = mInteractablesArray.indexOf(pItem);
			if(fIndexInter >= 0)
				mInteractablesArray.splice(fIndexInter,1);
			
			var fDepthIndex:int = mDepthManagedObjectsArray.indexOf( pItem );
			
			if(fDepthIndex >= 0)
				mDepthManagedObjectsArray.splice(fDepthIndex,1);
			
			if(pItem.displayObject != null)
			{
				if( this.contains(pItem.displayObject) )
					this.removeChild( pItem.displayObject );
			}
			
		}
		
		/**
		 * Get an Scene item from the scene 
		 * @param pItemId id of the item
		 * @return ISceneInteractable if the item is on the scene.
		 * 
		 */		
		public function GetSceneItem(pItemId:String):ISceneInteractable
		{
			var fRetItem:ISceneInteractable = null ;
			
			var fFindItem:Function = function(element:ISceneInteractable, index:int, arr:Array):Boolean
			{
				if(element.interactableId == pItemId)
					return true ;
					
				return false ;
			}
			
			var fResult:Array = mInteractablesArray.filter(fFindItem);
			
			if( fResult.length > 0 )
				fRetItem = fResult[0] ;
			
			return fRetItem ;
		}
		
		/**
		 * Add a new Scene Item to the Scene 
		 * @param pNewItem the new LItem to add in the Scene.
		 * 
		 */		
		public function addInteractableItem(pNewItem:ISceneInteractable):void
		{
			mInteractablesArray.push(pNewItem);
			this.addChild( pNewItem.displayObject);
			// We push it to this array so the depth in the Scene is managed.
			mDepthManagedObjectsArray.push( pNewItem );
		}
		
		/**
		 * Listener of the 	ADDED_TO_STAGE .
		 */
		protected function OnAddedToStage(event:Event):void
		{
			this.removeEventListener(Event.ADDED_TO_STAGE, OnAddedToStage );
			this.addEventListener(MouseEvent.CLICK, OnClick );
			this.addEventListener(Event.ENTER_FRAME,Update );
			
			this.addEventListener(Event.REMOVED_FROM_STAGE, OnRemovedFromStage );
			
			if(mDebugEnabled)
			{
				this.addEventListener(MouseEvent.MOUSE_MOVE,testMouseMove);
				this.addEventListener(MouseEvent.CLICK,DebugMouseClick);
			}
				
			this.addEventListener(MouseEvent.MOUSE_MOVE,OnMouseMove);
			stage.addEventListener(Event.RESIZE, resized);
			mStageWidth = stage.stageWidth ;
			mStageHeight = stage.stageHeight ;
			
			if(mDrawLayer == null)
			{
				mDrawLayer = new Sprite();
				this.addChild( mDrawLayer );
				
			}
			//trace(mStageWidth + " x " + mStageHeight );
			//InitScroll();
			
		}
		
		private function resized(e:Event):void
		{
			mStageWidth = stage.stageWidth ;
			mStageHeight = stage.stageHeight ;
		}
		
	
		
		private var mGestureMatched:Boolean = false ;
		
		
		
		protected function capturingHandler(e:Event):void
		{
			if(!mBookModeEnabled)
				return ;
				
			mDrawLayer.graphics.lineTo(mouseX,mouseY);
		}
		
		
		
		public function set bookMode(value:Boolean):void
		{
			mBookModeEnabled = value ;
			
			if(!mBookModeEnabled)
			{
				TweenLite.to( mDrawLayer,1,{alpha:0});
			}
				
		}
		
		public function get bookMode():Boolean
		{
			return mBookModeEnabled ;
		}
		
		public function get sceneId():String 
		{
			return mSceneId;
		}
		
		private function OnDrawLayerFadeOutComplete():void
		{
			mDrawLayer.graphics.clear();
		}
		
		////////////////////////////////////////
		
		/**
		 * Listener of the 	REMOVED_FROM_STAGE .
		 */
		protected function OnRemovedFromStage(event:Event):void
		{
			this.removeEventListener(Event.REMOVED_FROM_STAGE, OnRemovedFromStage );
			
			this.removeEventListener(MouseEvent.CLICK, OnClick );
			this.removeEventListener(Event.ENTER_FRAME, Update );
			
			
			if(mDebugEnabled)
			{
				this.removeEventListener(MouseEvent.MOUSE_MOVE,testMouseMove);
				this.removeEventListener(MouseEvent.CLICK,DebugMouseClick);
			}
			
			this.addEventListener(Event.ADDED_TO_STAGE, OnAddedToStage );
			
			
		}
				
		
		/**
		 * Method called when the players enters the Scene. 
		 * 
		 */		
		public function OnEnterScene():void
		{
			trace("OnEnterScene");
			
			
			this.addChild( mPlayerDisplay );
			mPlayerDisplay.sceneId = this.mSceneId ;
			UpdatePerspective();
			
			if(mAmbientSoundChan == null)
			{
				if(mAmbientSound != null)
				{
					mAmbientSoundChan = mAmbientSound.play(0,100);
					mAmbientSoundChan.addEventListener(Event.SOUND_COMPLETE, ReplayAmbient );
				}
			}
			
			mPlayerCharacter.sceneId = mSceneId ;
			SetInteractableEventListeners();
			
			// notify that the player's enters the scene
			var fNewEvent:SceneEvent = null ;
			fNewEvent = new SceneEvent( SceneEvent.ENTER_SCENE,
										this,
										mSceneId);
										
			this.dispatchEvent( Event(fNewEvent) );
			
			if( this.contains(mDrawLayer) )
				this.setChildIndex(mDrawLayer, this.numChildren - 1);
		}
		
		/**
		 * Method called when the players exits the Scene. 
		 * 
		 */		
		public function OnExitScene():void
		{
			if(mAmbientSoundChan != null)
				mAmbientSoundChan.stop();
			
			mPlayerDisplay.StopWalk();
			
			RemoveInteractableEventListeners();
			
			// notify that the player's exits the scene
			var fNewEvent:Event = null ;
			fNewEvent = new SceneEvent( SceneEvent.EXIT_SCENE,
										this,
										mSceneId);
										
			this.dispatchEvent( fNewEvent );
		}
		
		private function SetInteractableEventListeners():void
		{
			//trace("SetInteractableEventListeners")
			for each( var obint:ISceneInteractable in mInteractablesArray )
			{
				//trace("object:"+obint);
				obint.addEventListener( MouseEvent.MOUSE_OVER,OnObjectMouseOver);
				obint.addEventListener( MouseEvent.MOUSE_OUT,OnObjectMouseOut);
				obint.addEventListener( MouseEvent.CLICK,OnObjectClick);
				//obint.addEventListener( MouseEvent.DOUBLE_CLICK,OnObjectDoubleClick);
			}
		} 
		
		private function RemoveInteractableEventListeners():void
		{
			for each( var obint:ISceneInteractable in mInteractablesArray )
			{
				obint.removeEventListener( MouseEvent.MOUSE_OVER,OnObjectMouseOver);
				obint.removeEventListener( MouseEvent.MOUSE_OUT,OnObjectMouseOut);
				obint.removeEventListener( MouseEvent.CLICK,OnObjectClick);
				//obint.removeEventListener( MouseEvent.DOUBLE_CLICK,OnObjectDoubleClick);
			}
		} 
		
		private function OnObjectMouseOver(event:MouseEvent):void
		{
			
			if( (mCurrentSelectedInteractable != null) || (mCurrentSelectedInteractable == event.target))
				return ;
			
			
			var fCurrentTarget:ISceneInteractable = event.currentTarget as ISceneInteractable ;
			
			if( !fCurrentTarget.enabled )
				return ;
			
			mCurrentSelectedInteractable = fCurrentTarget ;

			var fNewEvent:SceneEvent = null ;
			fNewEvent = new SceneEvent( SceneEvent.SCENEITEM_MOUSEOVER,
										mCurrentSelectedInteractable,
										mSceneId,
										event.bubbles,
										event.cancelable,
										event.localX,
										event.localY,
										event.ctrlKey,
										event.altKey,
										event.shiftKey,
										this.mouseX,
										this.mouseY);
					//trace( "Scene OnObjectMouseOver "+ event.target  + " | "+mCurrentSelectedInteractable)		
			this.dispatchEvent( fNewEvent );
		}
		/* 
		private function GetSceneInteractable(pObject:DisplayObject):ISceneInteractable
		{
			if(pObject is ISceneInteractable)
			{
				return pObject as IScene;
			}
			
			if(pObject.parent is AbstractCharacterDisplay)
			{
				return pObject.parent as AbstractCharacterDisplay
			}
			
			return null
		}
		 */
		private function OnObjectMouseOut(event:MouseEvent):void
		{
			
			if(mCurrentSelectedInteractable == null)
			{
				return
			}
			
			if( mCurrentSelectedInteractable.displayObject.hitTestPoint(event.stageX,event.stageY,true) )
			{
				return ;
			}
			
			//if( !mCurrentSelectedInteractable.enabled )
			//	return ;
		
			//trace( "OnObjectMouseOut" )
			//trace( "Scene OnObjectMouseOut "+ event.target  )
			var fNewEvent:SceneEvent = null ;
			fNewEvent = new SceneEvent( SceneEvent.SCENEITEM_MOUSEOUT,
										mCurrentSelectedInteractable,
										mSceneId,
										event.bubbles,
										event.cancelable,
										event.localX,
										event.localY,
										event.ctrlKey,
										event.altKey,
										event.shiftKey,
										this.mouseX,
										this.mouseY);
										
			this.dispatchEvent( fNewEvent );
			mCurrentSelectedInteractable = null ;
		}
		
		
		private function OnObjectClick(event:MouseEvent):void
		{
			// if the Ctrl key and we are in debug mode, do nothing.
			if(event.ctrlKey && mDebugEnabled)
				return ;
				
			//trace( "OnObjectMouseClick" )
			
			var fCurrentTarget:ISceneInteractable = event.currentTarget as ISceneInteractable ;
			if( !fCurrentTarget.enabled )
				return ;
			mCurrentSelectedInteractable = fCurrentTarget ;
			
				
			var fNewEvent:SceneEvent = null ;
			fNewEvent = new SceneEvent( SceneEvent.SCENEITEM_CLICK,
										mCurrentSelectedInteractable,
										mSceneId,
										event.bubbles,
										event.cancelable,
										event.localX,
										event.localY,
										event.ctrlKey,
										event.altKey,
										event.shiftKey,
										this.mouseX,
										this.mouseY);
										
			this.dispatchEvent( fNewEvent );
		}
		/* 
		private function OnObjectDoubleClick(event:Event):void
		{
			trace( "OnObjectMouseDoubleClick" )
			mCurrentSelectedInteractable = event.target as ISceneInteractable ;
		}
		 */
		private function ReplayAmbient(event:Event):void
		{
			mAmbientSoundChan.removeEventListener(Event.SOUND_COMPLETE, ReplayAmbient );
			mAmbientSoundChan = mAmbientSound.play(0,100);
			mAmbientSoundChan.addEventListener(Event.SOUND_COMPLETE, ReplayAmbient );
			
		}
		
		
		// Test if a tile (IntPoint) is present in pArray.
		private function TileIsPresent(pArray:Array, pTileX:int, pTileY:int ):Boolean
		{
			var fFilterFunc:Function  = function (item:IntPoint, index:int, array:Array):Boolean
			{
				if((item.x == pTileX) && (item.y == pTileY))
				{
					return true ;
				}
				return false ;
			}
			
			var fResult:Array = pArray.filter(fFilterFunc);
			
			return (fResult.length > 0) ;
			
		}
		
		// removes a tile (IntPoint) from pArray.
		private function RemoveTile(pArray:Array, pTileX:int, pTileY:int ):void
		{
			var fIndex:int = -1 ;
			var fCurrentTile:IntPoint = null ;
			for(var i:int = 0 ; i < pArray.length; i++ )
			{
				fCurrentTile = pArray[i] as IntPoint ;
				
				if((fCurrentTile.x == pTileX) && (fCurrentTile.y == pTileY) )
				{
					fIndex = i ;
				}	
			}
			
			if( fIndex >= 0 )
				pArray.splice(fIndex,1);
		}
		
		// click event listener for debug mode
		private function DebugMouseClick(event:MouseEvent):void
		{
			if(event.ctrlKey)
			{
				var fTile:IntPoint = screenToMap(this.mouseX, this.mouseY);
				//mMapView.Highlight( fTile.x, fTile.y );
				
				if(TileIsPresent(mDebugTilesCollected,fTile.x, fTile.y) )
				{
					RemoveTile(mDebugTilesCollected, fTile.x, fTile.y);
					mMapView.Unhighlight( fTile.x, fTile.y, mDebugTilesCollected );
				}
				else
				{
					mMapView.Highlight( fTile.x, fTile.y );
					mDebugTilesCollected.push(fTile);
				}
				DebugSetClipboard(mDebugTilesCollected);
			}
		}
		
		// Set the clipboard with the tiles collected.
		private function DebugSetClipboard(pArray:Array):void
		{
			var fClipString:String = "" ;
			var fTileXML:XML = <IntPoint x="" y=""/>
			
			for each(var tile:IntPoint in pArray)
			{
				fTileXML.@x = tile.x ;
				fTileXML.@y = tile.y ;
				
				fClipString += fTileXML.toXMLString() + "\n" ;
			}
			
			System.setClipboard(fClipString);
				
		}
		
		// for debuging only
		private function testMouseMove(event:MouseEvent):void
		{
			//trace("Scene:" + this.mouseX + " " + this.mouseY );
			
			var fTile:IntPoint = screenToMap(this.mouseX, this.mouseY)
			//trace("Tile:" + fTile )
			
			mDebugTextField.text = "Scene:" + this.mouseX + ";" + this.mouseY + "\n" ;
			mDebugTextField.appendText( "Tile:" + fTile.x + ";" + fTile.y );
			
			if(mDebugTextField.hitTestPoint(event.stageX, event.stageY) )
			{
				if(mDebugTextFieldShift == 0 )
				{
					mDebugTextField.x = this.x + mDebugTextField.x + 100 ;
					mDebugTextFieldShift = 100 ;
				}
				else
				{
					mDebugTextField.x = this.x + mDebugTextField.x - 100 ;
					mDebugTextFieldShift = 0 ;
				}
				
				//trace("hit:"+mDebugTextField.x)
			}
			//mDebugTextField.x = event.stageX ;
			//mDebugTextField.y = event.stageY ;
		}
		
		private function OnMouseMove(event:MouseEvent):void
		{
			//trace(event.target);
			
			if( event.target is Scene)
			{
				if( mCurrentSelectedInteractable != null )
				{
					
					
					var fNewEvent:SceneEvent = null ;
					fNewEvent = new SceneEvent( SceneEvent.SCENEITEM_MOUSEOUT,
												mCurrentSelectedInteractable,
												mSceneId,
												event.bubbles,
												event.cancelable,
												event.localX,
												event.localY,
												event.ctrlKey,
												event.altKey,
												event.shiftKey,
												this.mouseX,
												this.mouseY);
												
						
					this.dispatchEvent( fNewEvent );
					mCurrentSelectedInteractable = null ;
				}
			}
		}
		
		
		/**
		 * 	Handle click, start solving 
		 */
		public function OnClick(event:MouseEvent):void
		{
			// if the Ctrl key and we are in debug mode, do nothing.
			if(event.ctrlKey && mDebugEnabled)
				return ;
			
			if(mBookModeEnabled)
				return ;
			
			if(mCurrentSelectedInteractable != null)
			{
				return ;
			}
			else
			{
				var fNewEvent:SceneEvent = null ;
				fNewEvent = new SceneEvent( SceneEvent.SCENE_SPACE_CLICK,
											null,
											mSceneId,
											event.bubbles,
											event.cancelable,
											event.localX,
											event.localY,
											event.ctrlKey,
											event.altKey,
											event.shiftKey,
											this.mouseX,
											this.mouseY);
				this.dispatchEvent( fNewEvent );
			}
			
			
			//MovePlayerDisplayTo(this.mouseX, this.mouseY);
			//MoveCharacterTo(mPlayerCharacter.characterId,this.mouseX, this.mouseY);
			
		}
		
		/**
		 * Moves the Scene's PlayerDisplay of the character in pCharId to the pX, pY coordinates by walking. 
		 * @param pX horizontal coordinate on the scene 
		 * @param pY vertical coordinate on the scene.
		 * 
		 */		
		public function MoveCharacterTo(pCharId:String, pX:Number, pY:Number, pOnCompleteFunc:Function = null):void
		{
			var fChar:AdventureCharacter = mCharactersOnScene[pCharId] as AdventureCharacter ;
			
			mOnPlayerWalkCompleteFunc = pOnCompleteFunc ;
			
			
			// if the character doesn't exist, ends.
			if(fChar == null)
				return ;
			
			// Get the character's display.
			var fCharDisplay:AbstractCharacterDisplay = fChar.characterDisplay ;
			
			// Reset the walk index
			fCharDisplay.walkIndex = 0 ;
			// reset the walk points. 
			fCharDisplay.walkPoints = new Array();
			
			if(mDebugEnabled)
			{
				// Clear, or we have rubble next time we click
				mOverlay.graphics.clear();
			}
			/* 
			if (!fCharDisplay.pfGoal) 
				fCharDisplay.pfGoal = fCharDisplay.currentTile ; // gets a copy of current tile.
			else
				fCharDisplay.pfGoal = screenToMap(fCharDisplay.x, fCharDisplay.y);
			 */	
			fCharDisplay.pfGoal = screenToMap(fCharDisplay.x, fCharDisplay.y);
			
			// Shift: arrival point is now starting point
			fCharDisplay.pfStart = fCharDisplay.pfGoal ;
			
			// Mark the clicked position as the goal
			fCharDisplay.pfGoal = screenToMap(pX, pY);
			
			if( mDebugEnabled )
			{
				mStartCursor.x = mapToScreenCoord( fCharDisplay.pfStart.x );
				mStartCursor.y = mapToScreenCoord( fCharDisplay.pfStart.y );
				
				mGoalCursor.x = mapToScreenCoord( fCharDisplay.pfGoal.x );
				mGoalCursor.y = mapToScreenCoord( fCharDisplay.pfGoal.y );
			}
			
			// if not walkable.
			if (!mTileMap.isWalkable(fCharDisplay.pfGoal.x,fCharDisplay.pfGoal.y)) 
			{
				// find a walkable goal.
				FindWalkableGoal(fCharDisplay.pfStart, fCharDisplay.pfGoal);		
			}
				
			var a:AStar = new AStar(mTileMap, fCharDisplay.pfStart, fCharDisplay.pfGoal);
			var solution:Array = a.solve();
			
			// Draw all visited nodes
			for (var j:int=0; j<a.visited.length; j++) {
				var vn:AStarNode = a.visited[j];
				drawLine(vn, vn.parent, 0xFF00FF);
			}
			
			// Do we have a path?
			if (solution) 
			{
				// YES! Loop and plot...
				var n:IntPoint = solution[0];
				for (var i:int = 1; i < solution.length; i++) 
				{
					fCharDisplay.walkPoints.push(n);
					var n2:IntPoint = n;
					n = solution[i];
					drawLine(n,n2,0xFFFFFF, 3);
					
				}
				
				
				
				drawLine(n,fCharDisplay.pfStart,0xFFFFFF,3);
			}
			
			
			fCharDisplay.walkPoints = CleanHeroRoute(fCharDisplay.walkPoints);
			//printDebug(mHeroWalkPoints);
			
			
			fCharDisplay.StartWalk( mOnPlayerWalkCompleteFunc );			
		}
		
		
		
		/**
		 * Find a walkable goal when the clicked tile is not walkable.
		 */
		protected function FindWalkableGoal( pStart:IntPoint, pGoal:IntPoint):void
		{
			var fDiff:IntPoint = new IntPoint();
			fDiff.x = pGoal.x - pStart.x ;
			fDiff.y = pGoal.y - pStart.y ;
			var fGreaterField:String = "" ;
			var fLesserField:String = "" ;
			var fSearchDirection:int = 0;
			
			
			
			if( Math.abs(fDiff.x) >  Math.abs(fDiff.y))
			{
				fGreaterField = 'x' ;
				fLesserField = 'y';
			}
			else
			{
				fGreaterField = 'y' ;
				fLesserField = 'x'
			}
			
			if(fDiff[fGreaterField] < 0)
			{
				fSearchDirection = 1 ; 
			}
			else if (fDiff[fGreaterField] > 0)
			{
				fSearchDirection = -1 ;
			}  
			
			var fNewGoal:IntPoint = new IntPoint( pGoal.x, pGoal.y);
			while(fNewGoal[fGreaterField] != pStart[fGreaterField])
			{
				fNewGoal[fGreaterField] = fNewGoal[fGreaterField] + fSearchDirection ;
				if( mTileMap.isWalkable(fNewGoal.x, fNewGoal.y ) )
				{
					pGoal.x = fNewGoal.x ;
					pGoal.y = fNewGoal.y ;
					return ;
				}
			}
			
			// now for the lesser field
			if(fDiff[fLesserField] < 0)
			{
				fSearchDirection = 1 ; 
			}
			else if (fDiff[fLesserField] > 0)
			{
				fSearchDirection = -1 ;
			} 
			
			while(fNewGoal[fLesserField] != pStart[fLesserField])
			{
				fNewGoal[fLesserField] = fNewGoal[fLesserField] + fSearchDirection ;
				if( mTileMap.isWalkable(fNewGoal.x, fNewGoal.y ) )
				{
					pGoal.x = fNewGoal.x ;
					pGoal.y = fNewGoal.y ;
					return ;
				}
			}
			
		}
		
		protected function OnPlayerWalkCompleted():void
		{
			trace("OnPlayerWalkCompleted");
			if(mOnPlayerWalkCompleteFunc != null)
			{
				mOnPlayerWalkCompleteFunc();
				mOnPlayerWalkCompleteFunc = null ;
			}
				
			DetectGateSpotsCollision();
		}
		
		public function DetectGateSpotsCollision():void
		{
			for each( var gspot:GateSpot in mGateSpotsArray )
			{
				//if( gspot.hitTestPoint( mPlayerDisplay.x, mPlayerDisplay.y,true) )
				if( gspot.hitTestObject( mPlayerDisplay ) && (gspot.enabled) )
				{
					//AdventureMaster.SceneMngr.SetScene( gspot.destinationSceneId );
					AdventureMaster.SceneMngr.SetSceneFromGateSpot( gspot, this.mSceneId );
				}
			}
		}
		
		
		protected function WalkDestinationReached():void
		{
			//DetectGateSpotsCollision();
			//trace( "WalkDestinationReached:" + mPlayerDisplay.x + " - "+ mPlayerDisplay.y );
			DetectGateSpotsCollision();
		}
		
		/**
		 * Clean the AStar results to have a continuous walk.
		 */ 
		protected function CleanHeroRoute( pTileRoute:Array ):Array
		{
			pTileRoute.reverse();
			if(pTileRoute.length <= 3)
				return pTileRoute;
			
			//trace("CleanHeroRoute: L:"+ pTileRoute.length );
			
			var fReturnArray:Array = new Array();
			var fPrevTile:IntPoint ;
			var fCurTile:IntPoint ;
			var fNextTile:IntPoint ;
			
			var fNextAngle:Number ;
			var fPrevAngle:Number ;
			
			
			fReturnArray.push( pTileRoute[0] );
			
			for( var i:int = 1 ; i < pTileRoute.length; i++)
			{
				fPrevTile = pTileRoute[i - 1] as IntPoint ;
				fCurTile = pTileRoute[i] as IntPoint ;
				
				if( (i+1) < (pTileRoute.length ))
					fNextTile = pTileRoute[i+1] as IntPoint;
				else
					fNextTile = null ;
				
				//trace("index "+i+ " "+fNextTile);	
				fPrevAngle = AngleInTwoIntPoints(fPrevTile, fCurTile );
				
				if( fNextTile != null )	
					fNextAngle = AngleInTwoIntPoints(fCurTile, fNextTile );
					
				if(fNextTile != null)
				{
					 if(fPrevAngle != fNextAngle)
					 {
					 	fReturnArray.push( fCurTile );
					 }
				}
				else
				{
					//trace("ultimo agregado:"+ fCurTile);
					fReturnArray.push( fCurTile )
				}	
				
				
			}
			
			return fReturnArray ;
			
		}
		
		/**
		 * Calculate the angle between two differents IntPoints.
		 * 
		 */ 
		protected function AngleInTwoIntPoints( pPointA:IntPoint, pPointB:IntPoint):Number
		{
			if((pPointA == null) || (pPointB == null) )
			{
				throw new Error("AngleInTwoPoints: One of the points is null");
			}
			
			var fDeltaX:Number = pPointB.x - pPointA.x;
			var fDeltaY:Number = pPointB.y - pPointA.y;
			
			var x2:Number = 1 ;
			var y2:Number = 0 ;
		
			var ret_angle:Number ;
		
			var partNumerador:Number ;
			var partDenominador:Number ;
		
			partNumerador = (fDeltaX * x2) + (fDeltaY * y2) ;
		
			partDenominador = Math.sqrt( (fDeltaX * fDeltaX) + (fDeltaY * fDeltaY) ) * Math.sqrt( (x2 * x2) + (y2 * y2) ) ;
		
			ret_angle = Math.acos( partNumerador / partDenominador );
		
			if( ((fDeltaX <= 0) && (fDeltaY < 0 )) || ( (fDeltaX >= 0) && (fDeltaY < 0 ) ) )
			{
				ret_angle = (Math.PI * 2) - ret_angle ;
			}
		
			return ret_angle ;
			
		}
		
		/**
		 * 	Draw a line from a to b
		 * 
		 */
		protected function drawLine(a:IntPoint, b:IntPoint, col:uint, width:Number=1):void {
			
			if(!mDebugEnabled)
				return ;
			
			var pa:Point = mapToScreen(a);
			var pb:Point = mapToScreen(b);
			
			mOverlay.graphics.lineStyle(width,col);
			mOverlay.graphics.moveTo(pa.x, pa.y);
			mOverlay.graphics.lineTo(pb.x, pb.y);
		}
		
		/**
		 * 	Return the center position of a tile in pixels
		 * 
		 */
		public function mapToScreen(p:IntPoint):Point
		{
			return new Point( (p.x*mTileSize)+(mTileSize/2), (p.y*mTileSize)+(mTileSize/2) );
		}
		
		/**
		 * 	Place the cursors on their positions.
		 * 
		 */
		protected function updateCursors():void
		{
			if(!mDebugEnabled)
				return ;
			
			
			var p:Point = new Point();
			if (mGoal) {
				p.x = mapToScreenCoord(mGoal.x);
				p.y = mapToScreenCoord(mGoal.y);
				
				mGoalCursor.x = p.x;
				mGoalCursor.y = p.y;
			}
			if(!mDebugEnabled)
				return;
			if (mStart) {
				p.x = mapToScreenCoord(mStart.x);
				p.y = mapToScreenCoord(mStart.y);
				mStartCursor.x = p.x;
				mStartCursor.y = p.y;
				
			}
			
			
			//mPlayerDisplay.x = mStartCursor.x ;
			//mPlayerDisplay.y = mStartCursor.y ;
		}
		
		/**
		 * 	On which tile is a point on the screen?
		 * 
		 */
		protected function screenToMap( pX:Number, pY:Number):IntPoint
		{
			return new IntPoint( Math.floor(pX/mTileSize), Math.floor(pY/mTileSize) );
		}
		
		/**
		 * 	Return the center position of a tile in pixels
		 *  works for both coordinates x or y.
		 */
		protected function mapToScreenCoord(pTileCoord:int):Number
		{
			return pTileCoord * mTileSize + (mTileSize / 2);
		}
		
		/**
		 * Place in the tile map the walk blocked or unblocked tiles received in pWalkBlockArray. 
		 * @param pTMap Tilemap to set the non walkable tiles.
		 * @param pWalkBlockArray list of IntPoint with the tiles to set non walkable. 
		 * @pWalkable if the tile should be walkable or not.
		 */
		protected function SetWalkBlockedTiles(pTMap:TileMap, pWalkBlockArray:Array, pWalkable:Boolean = false):void
		{
			for each( var tile:IntPoint in pWalkBlockArray )
			{
				pTMap.setWalkable( tile.x, tile.y, pWalkable );
			}
				
		}
		
		public function SetWalkTiles(pTilesArr:Array, pWalkable:Boolean):void
		{
			SetWalkBlockedTiles(mTileMap,pTilesArr,pWalkable);
		}
		
		/**
		 * 	Make a rounded to mark a position.
		 * 	@param size: size of the square marker.
		 *  @param col: color of the cursor.
		 */
		protected function createCursor(size:Number, col:uint):Shape
		{
			var s:Shape = new Shape();
			var g:Graphics = s.graphics;
			g.lineStyle(1,col);
			g.drawCircle(0,0, size);
			s.scaleY = 0.6;
			return s;
		}
		
		/**
		 * Get a scene GateSpot by destination. 
		 * @param pGatespotId gatespot id.
		 * @return GateSpot.
		 * 
		 */		
		public function GetGatespot(pGatespotId:String):GateSpot
		{
			var fRetItem:GateSpot = null ;
			
			var fFindItem:Function = function(element:GateSpot, index:int, arr:Array):Boolean
			{
				if(element.gatespotId == pGatespotId)
					return true ;
					
				return false ;
			}
			
			var fResult:Array = mGateSpotsArray.filter(fFindItem);
			
			if( fResult.length > 0 )
				fRetItem = fResult[0] ;
			
			return fRetItem ;
		}
		
	}
}